#pragma once

namespace AsyncLib
{

class CRefCounted
{
public:
	CRefCounted() : m_nRefCount(0)
	{
	}

	virtual ~CRefCounted() = default;

	CRefCounted(const CRefCounted&) = delete;
	CRefCounted& operator=(const CRefCounted&) = delete;

	void AddRef()
	{
		++m_nRefCount;
	}

	void Release()
	{
		if (--m_nRefCount == 0)
			delete this;
	}

private:
	std::atomic_uint m_nRefCount;
};

template<typename T>
class CAutoRefPtr
{
public:
	CAutoRefPtr(T *ptr = nullptr) : m_ptr(ptr)
	{
		if (m_ptr)
			m_ptr->AddRef();
	}

	~CAutoRefPtr()
	{
		if (m_ptr)
			m_ptr->Release();
	}

	CAutoRefPtr(const CAutoRefPtr& right) : CAutoRefPtr(right.m_ptr)
	{
	}

	CAutoRefPtr(CAutoRefPtr&& right) noexcept : CAutoRefPtr()
	{
		Swap(right);
	}

	CAutoRefPtr& operator=(const CAutoRefPtr& right)
	{
		CAutoRefPtr(right).Swap(*this);
		return *this;
	}

	CAutoRefPtr& operator=(CAutoRefPtr&& right) noexcept
	{
		CAutoRefPtr(std::move(right)).Swap(*this);
		return *this;
	}

	explicit operator bool() const noexcept
	{
		return m_ptr != nullptr;
	}

	T *operator->() const noexcept
	{
		assert(m_ptr);
		return m_ptr;
	}

	void operator()() const
		requires requires(T t) { t.Execute(); }
	{
		assert(m_ptr);

		if (m_ptr)
			m_ptr->Execute();
	}

private:
	void Swap(CAutoRefPtr& other) noexcept
	{
		std::swap(m_ptr, other.m_ptr);
	}

	T *m_ptr;
};

} // namespace AsyncLib
